const express = require('express');
const multer = require('multer');
const path = require('path');
const fs = require('fs');
const { v4: uuidv4 } = require('uuid');
const mammoth = require('mammoth');
const PizZip = require('pizzip');
const Docxtemplater = require('docxtemplater');
const ImageModule = require('docxtemplater-image-module-free');
const TurndownService = require('turndown');
const { exec, spawn } = require('child_process');
const xlsx = require('xlsx');
const axios = require('axios');
const cors = require('cors');
const { SSE } = require('express-sse');
require('dotenv').config(); // 加载.env环境变量
const logger = require('./utils/logger'); // 引入日志模块
const { splitdoc, getChapterContent } = require('./utils/splitdoc');
// 导入 DeepseekService
const DeepseekService = require('./service/deepseek_service'); 
// 导入 CCBFZ 服务
const CCBFZService = require('./service/ccbfz_service.js'); // 改为CommonJS导入方式
// 导入提示词模板
const { buildDocCheckPrompt, buildSectionCheckPrompt, buildTagCheckPrompt, mergeResults } = require('./prompt_templates/doccheck_prompt');

// 定义文档章节结构
const docSections = [
    {
        id: 'ch1',
        name: '背景说明',
        subsections: [
            { id: 'ch1_1', name: '编写目的' },
            { id: 'ch1_2', name: '术语定义' },
            { id: 'ch1_3', name: '参考资料' }
        ]
    },
    {
        id: 'ch2',
        name: '总体设计',
        subsections: [
            { id: 'ch2_1', name: '设计目标' },
            { id: 'ch2_2', name: '设计原则' },
            { id: 'ch2_3', name: '系统逻辑架构' },
            { id: 'ch2_4', name: '系统物理架构' }
        ]
    },
    {
        id: 'ch3',
        name: '详细设计',
        subsections: [
            { id: 'ch3_1', name: '功能设计' },
            { id: 'ch3_2', name: '数据结构设计' },
            { id: 'ch3_3', name: '接口设计' },
            { id: 'ch3_4', name: '安全设计' }
        ]
    },
    {
        id: 'ch4',
        name: '实现与部署',
        subsections: [
            { id: 'ch4_1', name: '开发环境' },
            { id: 'ch4_2', name: '部署说明' }
        ]
    }
];

// 存储任务日志的对象
const taskLogs = {};

const app = express();
const upload = multer({ dest: 'uploads/' });
const tempDir = path.join(__dirname, 'uploads'); // 定义临时目录路径
const turndownService = new TurndownService();
// 初始化 DeepseekService
const deepseekService = new DeepseekService();
// 初始化 CCBFZ 服务
const ccbfzService = new CCBFZService();

// 添加获取第一个可见工作表的辅助函数
function getFirstVisibleSheet(workbook) {
    if (!workbook || !workbook.SheetNames || workbook.SheetNames.length === 0) {
        throw new Error('无效的Excel工作簿或没有工作表');
    }
    
    // 首先尝试获取工作簿的Hidden属性信息
    const hidden = workbook.Workbook && workbook.Workbook.Sheets 
        ? workbook.Workbook.Sheets.map(s => s.Hidden) 
        : workbook.SheetNames.map(() => 0); // 如果没有Hidden属性，假设所有表都是可见的
    
    // 查找第一个未隐藏的工作表
    for (let i = 0; i < workbook.SheetNames.length; i++) {
        if (!hidden[i]) { // 0表示可见，1表示隐藏
            return workbook.SheetNames[i];
        }
    }
    
    // 如果所有表都隐藏了，则返回第一个工作表
    return workbook.SheetNames[0];
}

// 添加JSON解析中间件
app.use(express.json());

// 提供静态文件服务
app.use(express.static('static'));

// 添加对template目录的静态服务
app.use('/template', express.static(path.join(__dirname, 'template')));

// 添加对downloads目录的静态服务
app.use('/downloads', express.static(path.join(__dirname, 'public', 'downloads')));

// 添加Excel模板下载路由
app.get('/download-audit-template', (req, res) => {
    const templatePath = path.join(__dirname, 'template', 'DesignAuditTemple.xlsx');
    
    logger.info(`请求下载设计审核模板: DesignAuditTemple.xlsx`);
    
    if (!fs.existsSync(templatePath)) {
        logger.warn(`模板文件不存在: ${templatePath}`);
        return res.status(404).send('模板文件不存在');
    }
    
    try {
        logger.info(`开始下载设计审核模板: DesignAuditTemple.xlsx`);
        res.download(templatePath, 'DesignAuditTemple.xlsx', (err) => {
            if (err) {
                logger.error(`下载模板文件失败: ${err.message}`);
                return res.status(500).send('下载失败');
            }
        });
    } catch (error) {
        logger.error(`模板文件下载异常: ${error.message}`);
        res.status(500).send('服务器错误');
    }
});

app.get('/download-audit-template-with-prompt', (req, res) => {
    const templatePath = path.join(__dirname, 'template', 'DesignAuditTempleWithPrompt.xlsx');
    
    logger.info(`请求下载带prompt的设计审核模板: DesignAuditTempleWithPrompt.xlsx`);
    
    if (!fs.existsSync(templatePath)) {
        logger.warn(`模板文件不存在: ${templatePath}`);
        return res.status(404).send('模板文件不存在');
    }
    
    try {
        logger.info(`开始下载带prompt的设计审核模板: DesignAuditTempleWithPrompt.xlsx`);
        res.download(templatePath, 'DesignAuditTempleWithPrompt.xlsx', (err) => {
            if (err) {
                logger.error(`下载模板文件失败: ${err.message}`);
                return res.status(500).send('下载失败');
            }
        });
    } catch (error) {
        logger.error(`模板文件下载异常: ${error.message}`);
        res.status(500).send('服务器错误');
    }
});

// 请求日志中间件
app.use((req, res, next) => {
    logger.info(`${req.method} ${req.url} ${req.ip}`);
    next();
});

// 处理文件上传
app.post('/upload', upload.single('file'), async (req, res) => {
    if (!req.file) {
        logger.warn('没有文件上传');
        return res.status(400).json({ error: '没有文件上传' });
    }

    try {
        // 解码文件名，避免中文乱码
        const decodeFileName = (fileName) => {
            try {
                return Buffer.from(fileName, 'latin1').toString('utf8');
            } catch (e) {
                return fileName;
            }
        };
        
        const originalFileName = decodeFileName(req.file.originalname);
        const fileExtension = path.extname(originalFileName).toLowerCase();
        logger.info(`文件上传: ${originalFileName}，类型: ${fileExtension}`);
        
        if (fileExtension === '.docx') {
            // 处理Word文档
            logger.info(`开始处理Word文档: ${originalFileName}`);
            // 使用mammoth.convertToMarkdown直接将Word转换为Markdown，设置选项忽略图片
            const options = {
                convertImage: () => ({ 
                  type: "text",  // 替换为合法类型
                  value: "图片"      // 空内容
                }),
                idGenerator: {
                    generate: () => "" // 返回空字符串，禁用 ID 生成
                },
                transformDocument: (element) => {
                    if (element.type === "hyperlink" && 
                        element.destination?.toLowerCase().includes("_toc")) {
                        return { 
                            type: "text", 
                            value: element.children[0].value
                        };
                    }
                    return element; // 其他元素保持原样
                }
              };

            const wordResult = await mammoth.convertToMarkdown({ path: req.file.path }, options);
            const wordContent = wordResult.value.replace(/($$.*?$$)\s*$#_Toc\d+$/g, "$1").replace(/_Toc\d+/g, "");
            logger.info(`Word文档处理完成，转为Markdown成功，内容长度: ${wordContent.length}，已忽略图片`);

            // 保存Markdown内容到临时文件，使用英文文件名避免乱码
            const timestamp = new Date().getTime();
            const mdFilename = `markdown_${timestamp}.md`;
            const mdPath = path.join('uploads', mdFilename);
            fs.writeFileSync(mdPath, wordContent);

            // 删除上传的临时文件
            fs.unlinkSync(req.file.path);
            logger.info(`Word文档处理完成: ${originalFileName}`);

            // 返回转换后的HTML和Markdown文件信息
            res.json({ 
                success: true,
                content: wordContent,
                markdown: {
                    filename: mdFilename,
                    path: '/download/' + mdFilename
                }
            });
        } else if (fileExtension === '.xlsx') {
            // 处理Excel文件，只保存并返回文件名供后续处理
            logger.info(`Excel文件已上传: ${originalFileName}`);
            res.json({
                success: true,
                message: '已上传Excel文件',
                filename: originalFileName,
                tempPath: req.file.path
            });
        } else {
            // 不支持的文件类型
            logger.warn(`不支持的文件类型: ${fileExtension}`);
            fs.unlinkSync(req.file.path);  // 删除临时文件
            return res.status(400).json({ error: '不支持的文件类型，仅支持.docx和.xlsx文件' });
        }
    } catch (error) {
        // 确保清理临时文件
        logger.error(`文件处理异常: ${error.message}`);
        if (req.file && fs.existsSync(req.file.path)) {
            fs.unlinkSync(req.file.path);
        }
        res.status(500).json({ error: '文件转换失败：' + error.message });
    }
});

// 全局存储审核任务进度的对象
const reviewTaskProgress = {};

// 映射任务ID到对应的客户端连接
const taskClients = {};

// 发送审核项更新事件
function sendItemUpdateEvent(taskId, itemData) {
    // 查找连接到此任务的所有客户端
    const clients = taskClients[taskId] || [];
    
    if (clients.length === 0) return; // 没有客户端连接，直接返回
    
    try {
        // 获取类别
        const category = itemData.category || itemData['审查检查类别'] || '未知类别';
        
        // 获取章节名称，传入category
        const chapterName = getChapterNameFromPrompt(itemData.prompt || itemData.Prompt || '', category);
        
        // 分析结果确定结论类型（通过/警告/失败）
        const conclusion = determineConclusion(itemData.result || '');
        
        // 构建事件数据
        const eventData = {
            type: 'item_update',
            taskId: taskId,
            itemId: itemData.index,
            category: category,
            chapterName: chapterName,
            checkPoint: itemData['审查检查项'] || itemData.prompt || itemData.Prompt || `审核项 ${itemData.index+1}`,
            conclusion: conclusion,
            result: itemData.result || ''
        };
        
        // 向所有客户端发送更新
        clients.forEach(client => {
            client.write(`event: item_update\ndata: ${JSON.stringify(eventData)}\n\n`);
        });
    } catch (error) {
        logger.error(`发送审核项更新事件失败: ${error.message}`);
    }
}

// 从prompt中提取章节名称
function getChapterNameFromPrompt(prompt, category) {
    // 如果提供了category，则直接使用category作为章节名称
    if (category && category.trim()) {
        return category.trim();
    }
    
    // 向后兼容：如果没有提供category，尝试从prompt中提取章节信息
    const chapterMatch = prompt.match(/第(\d+)章|第(\d+)节|第(\d+)\.(\d+)节|章节[:：]?\s*(.+?)[\s，,]/);
    if (chapterMatch) {
        return chapterMatch[0];
    }
    
    // 获取【审核章节】标记，如果存在
    const auditChapterMatch = prompt.match(/【审核章节】：(.+?)(?=\n|$)/);
    if (auditChapterMatch && auditChapterMatch[1]) {
        return auditChapterMatch[1].trim();
    }
    
    return '其他';
}

// 根据审核结果确定结论类型
function determineConclusion(result) {
    // 使用正则表达式匹配"结论："或"【审核结论】："后面的内容
    const conclusionMatch = result.match(/(?:结论|【审核结论】)：\s*(\S+)/);
    
    if (conclusionMatch && conclusionMatch[1]) {
        const conclusion = conclusionMatch[1].trim();
        
        // 根据提取出的结论文本映射到对应的结论代码
        if (conclusion === '通过') {
            return 'pass';
        } else if (conclusion === '不通过') {
            return 'fail';
        } else if (conclusion === '不涉及') {
            return 'notApplicable';
        } 
    }
    
    // 如果没有匹配到结论或格式不符合预期，返回默认值
    return 'warn'; // 默认为警告状态
}

// 为任务添加日志
function addTaskLog(taskId, logLevel, message) {
    if (reviewTaskProgress[taskId]) {
        // 确保logs数组存在
        if (!reviewTaskProgress[taskId].logs) {
            reviewTaskProgress[taskId].logs = [];
        }
        
        // 添加带时间戳的日志
        const timestamp = new Date().toLocaleTimeString();
        const logEntry = `[${timestamp}] [${logLevel.toUpperCase()}] ${message}`;
        
        // 记录日志
        reviewTaskProgress[taskId].logs.push(logEntry);
        
        // 限制日志数量，避免内存占用过大
        if (reviewTaskProgress[taskId].logs.length > 100) {
            // 只保留最近的100条日志
            reviewTaskProgress[taskId].logs = reviewTaskProgress[taskId].logs.slice(-100);
        }
        
        // 同时记录到服务器日志，确保日志级别存在
        try {
            const validLogLevels = ['info', 'warn', 'error', 'debug', 'success'];
            const normalizedLevel = logLevel.toLowerCase();
            // 如果日志级别不存在，默认使用info
            const level = validLogLevels.includes(normalizedLevel) ? normalizedLevel : 'info';
            // success级别logger中不存在，映射为info
            const loggerLevel = level === 'success' ? 'info' : level;
            logger[loggerLevel](`任务 ${taskId}: ${message}`);
        } catch (error) {
            console.error(`记录日志失败: ${error.message}`);
            logger.error(`记录日志失败: ${error.message}`);
        }
    }
}

// 获取审核任务进度
app.get('/review-progress/:taskId', (req, res) => {
    const { taskId } = req.params;
    const taskStatus = reviewTaskProgress[taskId];
    logger.info(`获取审核任务进度: ${taskId}, 状态: ${taskStatus ? taskStatus.status : '未找到'}`);
    
    if (!taskStatus) {
        logger.warn(`未找到审核任务: ${taskId}`);
        return res.status(404).json({ error: '未找到任务' });
    }
    
    // 构建响应对象，确保包含initItems字段
    const response = {
        ...taskStatus,
        // 如果initItems存在，保留它；否则设为空数组
        initItems: taskStatus.initItems || []
    };
    
    res.json(response);
});

// 提供SSE（Server-Sent Events）接口，用于实时更新审核项状态
app.get('/review-events/:taskId', (req, res) => {
    const taskId = req.params.taskId;
    
    logger.info(`建立审核任务SSE连接: ${taskId}`);
    
    // 设置SSE响应头
    res.setHeader('Content-Type', 'text/event-stream');
    res.setHeader('Cache-Control', 'no-cache');
    res.setHeader('Connection', 'keep-alive');
    res.setHeader('Access-Control-Allow-Origin', '*');
    
    // 将当前客户端连接与任务ID关联
    if (!taskClients[taskId]) {
        taskClients[taskId] = [];
    }
    taskClients[taskId].push(res);
    
    // 立即发送初始化事件
    res.write(`event: connected\ndata: {"taskId": "${taskId}"}\n\n`);
    
    // 获取任务信息
    const taskInfo = reviewTaskProgress[taskId];
    
    // 如果任务存在并且已初始化Excel数据，发送所有审核项的初始信息
    if (taskInfo) {
        try {
            // 优先使用已经预构建的初始化项目列表
            if (taskInfo.initItems && taskInfo.initItems.length > 0) {
                // 发送初始化事件包含所有审核项
                res.write(`event: init_items\ndata: ${JSON.stringify(taskInfo.initItems)}\n\n`);
                logger.info(`已发送${taskInfo.initItems.length}个预构建的审核项初始化信息`);
            } 
            // 如果没有预构建列表，则尝试读取Excel文件
            else {
                // 获取Excel数据文件路径
                let excelFilePath;
                if (taskInfo.useBaseAudit) {
                    excelFilePath = path.join(__dirname, 'template', 'DesignAuditTempleWithPrompt.xlsx');
                } else if (taskInfo.useSDTemplateAudit) {
                    excelFilePath = path.join(__dirname, 'template', 'DesignAuditTempleSD.xlsx');
                } else if (taskInfo.excelFilePath) {
                    excelFilePath = taskInfo.excelFilePath;
                }
                
                if (excelFilePath && fs.existsSync(excelFilePath)) {
                    // 读取Excel文件
                    const workbook = xlsx.readFile(excelFilePath);
                    const sheetName = getFirstVisibleSheet(workbook);
                    const worksheet = workbook.Sheets[sheetName];
                    const data = xlsx.utils.sheet_to_json(worksheet);
                    
                    if (data && data.length > 0) {
                        // 构建初始化项目列表
                        const initItems = data.map((row, index) => {
                            const prompt = row.prompt || row.Prompt || `审核项 ${index+1}`;
                            const category = row['审查检查类别'] || row.category || '未知类别';
                            // 提取章节名，传入category
                            const chapterName = getChapterNameFromPrompt(prompt, category);
                            return {
                                type: 'init_item',
                                itemId: index,
                                category: category,
                                chapterName: chapterName,
                                checkPoint: row['审查检查项'] || prompt,
                                conclusion: null,
                                result: null
                            };
                        });
                        
                        // 发送初始化事件包含所有审核项
                        res.write(`event: init_items\ndata: ${JSON.stringify(initItems)}\n\n`);
                        logger.info(`已发送${initItems.length}个审核项初始化信息`);
                    }
                }
            }
        } catch (error) {
            logger.error(`发送初始化项目列表时出错: ${error.message}`);
        }
    }
    
    // 如果任务存在且已有审核结果，立即发送当前结果
    if (taskInfo && taskInfo.results) {
        const results = taskInfo.results;
        
        // 为每个已有的结果发送一个更新事件
        results.forEach(result => {
            sendItemUpdateEvent(taskId, result);
        });
    }
    
    // 客户端断开连接时，从映射中移除
    req.on('close', () => {
        logger.info(`客户端断开SSE连接: ${taskId}`);
        if (taskClients[taskId]) {
            const index = taskClients[taskId].indexOf(res);
            if (index !== -1) {
                taskClients[taskId].splice(index, 1);
            }
            // 如果没有更多客户端，清理资源
            if (taskClients[taskId].length === 0) {
                delete taskClients[taskId];
            }
        }
    });
});

// 处理文档审核检查
app.post('/process-review', upload.fields([
    { name: 'wordFile', maxCount: 1 },
    { name: 'excelFile', maxCount: 1 }
]), async (req, res) => {
    try {
        const modelType = req.body.modelType || 'deepseek-reasoner';
        const wordFile = req.files?.wordFile?.[0];
        const excelFile = req.files?.excelFile?.[0];
        const useBaseAudit = req.body.useBaseAudit === 'true';
        const useSDTemplateAudit = req.body.useSDTemplateAudit === 'true';
        
        logger.info(`收到文档审核请求: 模型类型=${modelType}, 使用基础审核项=${useBaseAudit}, 使用实调审核项=${useSDTemplateAudit}`);

        if (!wordFile) {
            logger.warn('文档审核请求缺少Word文件');
            return res.status(400).json({ error: '请提供Word文档' });
        }
        
        if (!useBaseAudit && !useSDTemplateAudit && !excelFile) {
            logger.warn('自定义审核模式下缺少Excel文件');
            return res.status(400).json({ error: '自定义审核模式下请提供Excel文件' });
        }
        
        // 解决中文文件名显示问题，使用Buffer转换避免乱码
        const decodeFileName = (fileName) => {
            try {
                // 尝试解码文件名
                return Buffer.from(fileName, 'latin1').toString('utf8');
            } catch (e) {
                return fileName; // 如果解码失败，返回原始文件名
            }
        };
        
        const wordFileName = decodeFileName(wordFile.originalname);
        let excelFileName;
        
        if (useBaseAudit) {
            excelFileName = 'DesignAuditTempleWithPrompt.xlsx';
        } else if (useSDTemplateAudit) {
            excelFileName = 'DesignAuditTempleSD.xlsx';
        } else {
            excelFileName = decodeFileName(excelFile.originalname);
        }
        
        logger.info(`文档审核文件: Word=${wordFileName}, Excel=${excelFileName}, 使用基础审核项=${useBaseAudit}, 使用实调审核项=${useSDTemplateAudit}`);

        // 创建任务ID
        const taskId = Date.now().toString();
        logger.info(`创建审核任务: ${taskId}`);
        
        try {
            // 1. 先处理Word文档，转为MD文本
            logger.info(`开始处理Word文档: ${wordFileName}`);
            // 初始化任务进度和日志
            reviewTaskProgress[taskId] = {
                status: 'processing',
                total: 0,
                completed: 0,
                results: [],
                logs: []
            };
            
            addTaskLog(taskId, 'info', `开始处理Word文档: ${wordFileName}`);
            // 使用两步转换：先转为HTML，再转为Markdown，这样可以避免mammoth.convertToMarkdown的问题
            const options = {
                convertImage: () => ({ 
                  type: "text",  // 替换为合法类型
                  value: "图片"      // 空内容
                }),
                idGenerator: {
                    generate: () => "" // 返回空字符串，禁用 ID 生成
                },
                transformDocument: (element) => {
                    if (element.type === "hyperlink" && 
                        element.destination?.toLowerCase().includes("_toc")) {
                        return { 
                            type: "text", 
                            value: element.children[0].value
                        };
                    }
                    return element; // 其他元素保持原样
                }
              };
            
              const wordResult = await mammoth.convertToMarkdown({ path: wordFile.path }, options);
              const wordContent = wordResult.value.replace(/($$.*?$$)\s*$#_Toc\d+$/g, "$1").replace(/_Toc\d+/g, "");
              
              // 对文档进行章节切割，便于审核时按章节使用
              const documentChapters = splitdoc(wordContent);
              
              // 保存文档章节映射到任务信息中，以便后续使用
              reviewTaskProgress[taskId].documentChapters = documentChapters;
              
              addTaskLog(taskId, 'info', `Word文档已转换为Markdown，共 ${wordContent.length} 个字符，已切割为 ${Object.keys(documentChapters).length} 个章节`);
           
            
            addTaskLog(taskId, 'info', `Word文档处理完成，转为Markdown成功，内容长度: ${wordContent.length}，已忽略图片`);
            
            // 2. 处理Excel文件，获取prompt列
            addTaskLog(taskId, 'info', `开始处理Excel文件: ${excelFileName}`);
            
            let workbook, excelFilePath;
            
            if (useBaseAudit) {
                // 使用基础审核项模板
                excelFilePath = path.join(__dirname, 'template', 'DesignAuditTempleWithPrompt.xlsx');
                addTaskLog(taskId, 'info', `使用基础审核项模板: ${excelFilePath}`);
                workbook = xlsx.readFile(excelFilePath);
            } else if (useSDTemplateAudit) {
                // 使用实调审核项模板
                excelFilePath = path.join(__dirname, 'template', 'DesignAuditTempleSD.xlsx');
                addTaskLog(taskId, 'info', `使用实调审核项模板: ${excelFilePath}`);
                workbook = xlsx.readFile(excelFilePath);
            } else {
                // 使用上传的Excel文件
                excelFilePath = excelFile.path;
                workbook = xlsx.readFile(excelFilePath);
            }
            
            // 获取第一个未隐藏的工作表
            const sheetName = getFirstVisibleSheet(workbook);
            const worksheet = workbook.Sheets[sheetName];
            const data = xlsx.utils.sheet_to_json(worksheet);
            
            if (data.length === 0) {
                addTaskLog(taskId, 'error', 'Excel文件中未找到有效数据');
                return res.status(400).json({ error: 'Excel文件中未找到有效数据' });
            }
            
            addTaskLog(taskId, 'info', `从Excel中读取到 ${data.length} 行数据`);
            
            // 更新任务总数
            reviewTaskProgress[taskId].total = data.length;
            
            // 保存Excel文件路径到任务信息，以便SSE连接时读取
            reviewTaskProgress[taskId].excelFilePath = excelFilePath;
            
            // 预先构建初始化项目列表并保存到任务信息中
            const initItems = data.map((row, index) => {
                const prompt = row.prompt || row.Prompt || `审核项 ${index+1}`;
                const category = row['审查检查类别'] || row.category || '未知类别';
                // 提取章节名，传入category
                const chapterName = getChapterNameFromPrompt(prompt, category);
                return {
                    type: 'init_item',
                    itemId: index,
                    category: category,
                    chapterName: chapterName,
                    checkPoint: row['审查检查项'] || prompt,
                    conclusion: null,
                    result: null
                };
            });
            reviewTaskProgress[taskId].initItems = initItems;
            
            // 3. 先返回任务ID，让前端开始轮询
            res.json({ taskId: taskId });
            
            // 4. 后台处理审核任务
            addTaskLog(taskId, 'info', `开始执行审核任务，使用模型: ${modelType}`);
            
            // 并行处理审核任务
            try {
                // 设置并行批次大小
                const batchSize = parseInt(process.env.MODEL_BATCH_SIZE) || 9; // 从环境变量获取批次大小，默认为3
                
                // 按批次处理数据
                for (let batchStart = 0; batchStart < data.length; batchStart += batchSize) {
                    const batchEnd = Math.min(batchStart + batchSize, data.length);
                    addTaskLog(taskId, 'info', `处理批次 ${batchStart+1}-${batchEnd}/${data.length}`);
                    
                    // 创建批次任务
                    const batchTasks = [];
                    
                    for (let i = batchStart; i < batchEnd; i++) {
                        batchTasks.push(
                            (async (index) => {
                                const row = data[index];
                                const prompt = row.prompt || row.Prompt || `请审核第${index+1}行内容`;
                                
                                addTaskLog(taskId, 'info', `开始处理第 ${index+1}/${data.length} 行，使用模型: ${modelType}`);
                                
                                try {
                    // 根据选择的模型调用不同的API
                    let apiResult;
                    
                    // 获取类别和提示词
                    const prompt = row.prompt || row.Prompt || `请审核第${index+1}行内容`;
                    const category = row['审查检查类别'] || '';
                    
                    // 从任务信息中获取预先切割好的文档章节映射
                    const documentChapters = reviewTaskProgress[taskId].documentChapters || {};
                    
                    // 根据category和prompt获取对应章节内容
                    const chapterContent = getChapterContent(wordContent, prompt, category, documentChapters);
                    
                    addTaskLog(taskId, 'info', `审核第 ${index+1} 行，类别: ${category || '未知类别'}，使用${prompt.includes('审核章节') || category ? '章节内容' : '全文内容'}进行分析`);
                    
                    // 根据modelType判断使用哪个服务
                    if (modelType === 'deepseek-reasoner') {
                        // 使用公网Deepseek R1
                        addTaskLog(taskId, 'info', `调用公网Deepseek R1模型，处理第 ${index+1} 行...`);
                        apiResult = await deepseekService.reviewDocument(chapterContent, prompt, modelType);
                        addTaskLog(taskId, 'success', `公网Deepseek R1模型返回成功，结果长度: ${(apiResult.result || '').length}`);
                        
                        // 添加模型响应文本摘要
                        const resultContent = apiResult.result || '';
                        if (resultContent.length > 0) {
                            // 截取前200个字符作为摘要
                            const previewText = resultContent.substring(0, 200) + (resultContent.length > 200 ? '...' : '');
                            addTaskLog(taskId, 'info', `模型响应摘要: "${previewText}"`);
                        }
                        
                        // 添加思考过程摘要(如果有)
                        if (apiResult.thinking && apiResult.thinking.length > 0) {
                            const thinkingPreview = apiResult.thinking.substring(0, 100) + (apiResult.thinking.length > 100 ? '...' : '');
                            addTaskLog(taskId, 'info', `思考过程摘要: "${thinkingPreview}"`);
                        }
                    } else {
                        // 使用建行方舟模型（满血版或蒸馏版）
                        addTaskLog(taskId, 'info', `调用建行方舟模型: ${modelType}，处理第 ${index+1} 行...`);
                        apiResult = await ccbfzService.reviewDocument(chapterContent, prompt, modelType);
                        addTaskLog(taskId, 'success', `建行方舟模型返回成功，结果长度: ${(apiResult.result || '').length}`);
                        
                        // 添加模型响应文本摘要
                        const resultContent = apiResult.result || '';
                        if (resultContent.length > 0) {
                            // 截取前200个字符作为摘要
                            const previewText = resultContent.substring(0, 200) + (resultContent.length > 200 ? '...' : '');
                            addTaskLog(taskId, 'info', `模型响应摘要: "${previewText}"`);
                        }
                        
                        // 添加思考过程摘要(如果有)
                        if (apiResult.thinking && apiResult.thinking.length > 0) {
                            const thinkingPreview = apiResult.thinking.substring(0, 100) + (apiResult.thinking.length > 100 ? '...' : '');
                            addTaskLog(taskId, 'info', `思考过程摘要: "${thinkingPreview}"`);
                        }
                    }
                    
                                    // 返回处理结果
                                    return {
                                        index,
                        result: apiResult.answer || apiResult.result,
                        thinking: apiResult.thinking
                                    };
                                } catch (error) {
                                    addTaskLog(taskId, 'error', `处理第 ${index+1} 行时出错: ${error.message}`);
                                    // 返回错误结果
                                    return {
                                        index,
                                        result: `处理错误: ${error.message}`,
                                        thinking: '处理过程中出现错误'
                                    };
                                }
                            })(i)
                        );
                    }
                    
                    // 并行执行当前批次
                    const batchResults = await Promise.all(batchTasks);
                    
                    // 更新数据和进度
                    for (const result of batchResults) {
                        data[result.index].result = result.result;
                        data[result.index].thinking = result.thinking;
                        
                        // 更新任务进度
                    reviewTaskProgress[taskId].completed++;
                        reviewTaskProgress[taskId].results.push({
                            index: result.index, // 添加原始索引信息，用于后续排序
                            prompt: data[result.index].prompt || data[result.index].Prompt,
                            result: result.result,
                            thinking: result.thinking
                        });
                        
                        // 发送审核项更新事件
                        sendItemUpdateEvent(taskId, {
                            index: result.index,
                            category: data[result.index]['审查检查类别'] || '未知类别',
                            '审查检查项': data[result.index]['审查检查项'] || '',
                            prompt: data[result.index].prompt || data[result.index].Prompt,
                            result: result.result,
                            thinking: result.thinking
                        });
                        
                        addTaskLog(taskId, 'info', `第 ${result.index+1} 行处理完成，进度: ${reviewTaskProgress[taskId].completed}/${reviewTaskProgress[taskId].total}`);
                    }
                    
                    // 在批次之间添加短暂延迟，避免API请求过于集中
                    await new Promise(resolve => setTimeout(resolve, 200));
                }
                
                addTaskLog(taskId, 'success', '所有审核项并行处理完成');
                
                // 对结果数组按原始索引排序，确保顺序与Excel中行顺序一致
                if (reviewTaskProgress[taskId].results && reviewTaskProgress[taskId].results.length > 0) {
                    addTaskLog(taskId, 'info', '正在对结果进行排序，确保与原始顺序一致...');
                    reviewTaskProgress[taskId].results.sort((a, b) => a.index - b.index);
                    
                    // 确保data数组中的结果与排序后的结果一致
                    for (const sortedResult of reviewTaskProgress[taskId].results) {
                        if (sortedResult.index !== undefined && sortedResult.index < data.length) {
                            // 将排序后的结果重新写回data数组
                            data[sortedResult.index].result = sortedResult.result;
                            data[sortedResult.index].thinking = sortedResult.thinking;
                        }
                    }
                    addTaskLog(taskId, 'info', '结果已按原始顺序重新同步到数据中');
                }
            } catch (batchError) {
                addTaskLog(taskId, 'error', `批处理过程中发生错误: ${batchError.message}`);
                logger.error(`批处理错误: ${batchError.message}`);
                logger.error(batchError.stack);
            }
            
            // 5. 处理完成后，生成结果文件
            addTaskLog(taskId, 'info', '所有审核项处理完成，正在生成结果文件...');
            
            // 创建新的工作表
            const newWorkbook = xlsx.utils.book_new();
            const newWorksheet = xlsx.utils.json_to_sheet(data);
            xlsx.utils.book_append_sheet(newWorkbook, newWorksheet, sheetName);
            
            // 保存到新文件 - 使用英文名称避免乱码问题，但保留原始中文文件名信息
            const timestamp = Date.now();
            // 从原文件名中提取基本名称（不含扩展名），避免使用特殊字符
            const safeWordFileName = wordFileName.replace(/[^a-zA-Z0-9\u4e00-\u9fa5]/g, '_').substring(0, 30);
            const outputFilename = `audit_results_${safeWordFileName}_${timestamp}.xlsx`;
            const outputPath = path.join('uploads', outputFilename);
            xlsx.writeFile(newWorkbook, outputPath);
            
            // 更新任务状态
            reviewTaskProgress[taskId].status = 'completed';
            reviewTaskProgress[taskId].message = `审核完成，共处理 ${data.length} 个检查项`;
            reviewTaskProgress[taskId].downloadPath = '/download-excel/' + outputFilename;
            
            addTaskLog(taskId, 'success', `审核任务完成，结果文件已生成: ${outputFilename}`);
            
        } catch (error) {
            logger.error(`审核任务执行失败: ${error.message}`);
            logger.error(error.stack);
            
            if (reviewTaskProgress[taskId]) {
                addTaskLog(taskId, 'error', `审核任务执行失败: ${error.message}`);
                // 更新任务状态为失败
                reviewTaskProgress[taskId].status = 'failed';
                reviewTaskProgress[taskId].error = error.message;
            }
            
            // 由于响应已经发送，这里不再返回错误信息
        } finally {
            // 清理临时文件
            if (wordFile && fs.existsSync(wordFile.path)) {
                fs.unlinkSync(wordFile.path);
                addTaskLog(taskId, 'info', '清理临时Word文件');
            }
            if (excelFile && fs.existsSync(excelFile.path)) {
                fs.unlinkSync(excelFile.path);
                addTaskLog(taskId, 'info', '清理临时Excel文件');
            }
        }
    } catch (error) {
        logger.error(`处理审核请求失败: ${error.message}`);
        res.status(500).json({ error: '处理审核请求失败: ' + error.message });
    }
});

// 调用大模型API的函数
async function callModelAPI(modelType, prompt, content) {
    try {
        // 检查是否为Deepseek模型ID
        if (modelType === 'deepseek-reasoner') {
            logger.info('使用公网Deepseek模型API进行审核');
            return await deepseekService.reviewDocument(content, prompt, modelType);
        } 
        // 检查是否为建行方舟模型ID
        else if (modelType.includes('Deeseek-R1') || modelType.includes('DeepSeek-R1')) {
            logger.info(`使用建行方舟模型API进行审核: ${modelType}`);
            return await ccbfzService.reviewDocument(content, prompt, modelType);
        }
        // 其他模型返回模拟响应 
        else {
            logger.info(`使用模拟响应模式: ${modelType}`);
            await new Promise(resolve => setTimeout(resolve, 500 + Math.random() * 2000));
            return {
                result: `对"${prompt.substring(0, 30)}..."的审核结果: 符合要求。`,
                thinking: `分析过程：根据输入提示"${prompt.substring(0, 30)}..."，检查了相关内容，结果符合要求。`
            };
        }
    } catch (error) {
        logger.error(`API调用失败: ${error.message}`);
        logger.error(error.stack);
        console.error('API调用失败:', error);
        throw new Error(`API调用失败: ${error.message}`);
    }
}

// 直接使用 Node.js 调用 DeepSeek API
async function callDeepseekAPI(prompt, content) {
    try {
        logger.info('使用Node.js直接调用DeepSeek API...');
        console.log('使用Node.js直接调用DeepSeek API...');
        
        // 使用我们的 DeepseekService 进行文档审核
        const result = await deepseekService.reviewDocument(content, prompt);
        
        logger.info('DeepSeek API调用成功');
        
        return {
            answer: result.result,
            thinking: result.thinking || ''
        };
    } catch (error) {
        logger.error(`调用DeepSeek API时发生错误: ${error.message}`);
        
        // 如果API调用失败，返回一个模拟响应，而不是抛出错误
        console.warn('API调用失败，返回模拟响应');
        return {
            answer: `【API调用失败】无法连接到DeepSeek API(${error.message})。请检查：\n1. API密钥是否正确配置\n2. 网络连接是否正常\n3. .env中的DEEPSEEK_API_KEY和DEEPSEEK_API_URL配置是否正确`,
            thinking: `尝试调用DeepSeek API失败，错误信息: ${error.message}`
        };
    }
}

// 流式聊天API接口
app.post('/api/chat-stream', async (req, res) => {
    try {
        const { model, messages, temperature = 0.7, max_tokens = 2048 } = req.body;
        
        if (!messages || !Array.isArray(messages) || messages.length === 0) {
            return res.status(400).json({ error: '消息格式不正确' });
        }
        
        // 设置响应头，支持流式输出
        res.setHeader('Content-Type', 'text/plain; charset=utf-8');
        res.setHeader('Transfer-Encoding', 'chunked');
        res.setHeader('Connection', 'keep-alive');
        res.setHeader('Cache-Control', 'no-cache');
        
        // 获取API配置信息
        let apiKey, apiUrl;
        if (model === 'deepseek') {
            apiKey = process.env.DEEPSEEK_API_KEY;
            apiUrl = process.env.DEEPSEEK_API_URL || 'https://api.deepseek.com/v1/reasoner/completions';
            console.log('使用Deepseek模型进行聊天');
        } else { // qwen
            apiKey = process.env.QWEN_API_KEY;
            apiUrl = process.env.QWEN_API_URL || 'https://api.qwen.ai/v1/chat/completions';
            console.log('使用Qwen模型进行聊天');
        }
        
        // 检查API密钥是否配置
        if (!apiKey || apiKey === 'your_deepseek_key_here' || apiKey === 'your_qwen_key_here') {
            console.warn(`警告: ${model}模型API密钥未正确配置，使用模拟响应流`); 
            // 模拟流式响应
            simulateStreamResponse(res, messages[messages.length - 1].content);
            return;
        }

        // 实际的API调用
        try {
            const response = await axios.post(apiUrl, {
                model: model === 'deepseek' ? 'deepseek-reasoner' : 'qwen-2.5',
                messages: messages,
                stream: true,
                temperature: temperature,
                max_tokens: max_tokens
            }, {
                headers: {
                    'Authorization': `Bearer ${apiKey}`,
                    'Content-Type': 'application/json'
                },
                responseType: 'stream'
            });
            
            // 将API响应流转发到客户端
            response.data.pipe(res);
            
            // 处理完成时关闭连接
            response.data.on('end', () => {
                console.log('流式响应完成');
                res.end();
            });
            
        } catch (error) {
            console.error('API调用失败:', error.message);
            // 如果API调用失败，回退到模拟响应
            simulateStreamResponse(res, messages[messages.length - 1].content);
        }
    } catch (error) {
        console.error('处理聊天请求失败:', error);
        res.status(500).json({ error: '服务器处理请求失败' });
    }
});

// 模拟流式响应的函数
function simulateStreamResponse(res, userMessage) {
    console.log('API调用失败，使用模拟流式响应');
    logger.warn('API调用失败，使用模拟流式响应');
    
    // 根据用户输入生成一个简单的响应
    const responseText = "API调用失败，请检查API密钥配置和网络连接。您可以在.env文件中设置正确的API密钥。" + 
                         "目前使用模拟响应代替。\n\n" + 
                         "问题可能原因：\n" + 
                         "1. API密钥未设置或不正确\n" + 
                         "2. 网络连接问题\n" + 
                         "3. API服务不可用\n\n" + 
                         "请联系管理员解决此问题。";
    
    // 将响应文本拆分为字符
    const chars = responseText.split('');
    
    // 模拟流式输出，每50毫秒发送一个字符
    let index = 0;
    const interval = setInterval(() => {
        if (index < chars.length) {
            // 发送单个字符
            res.write(chars[index]);
            index++;
        } else {
            // 发送完成，清除定时器并结束响应
            clearInterval(interval);
            res.end();
        }
    }, 50);
}

// 下载Excel文件
app.get('/download-excel/:filename', (req, res) => {
    const filename = req.params.filename;
    const filepath = path.join('uploads', filename);
    
    logger.info(`请求下载Excel文件: ${filename}`);
    
    if (!fs.existsSync(filepath)) {
        logger.warn(`Excel文件不存在: ${filepath}`);
        return res.status(404).send('文件不存在或已过期');
    }
    
    try {
        logger.info(`开始下载Excel文件: ${filename}`);
        res.download(filepath, filename, (err) => {
            if (err) {
                logger.error(`下载Excel文件失败: ${err.message}`);
                return res.status(500).send('下载失败');
            }
            
            // 下载完成后删除文件（可选）
            // fs.unlinkSync(filepath);
        });
    } catch (error) {
        logger.error(`Excel文件下载异常: ${error.message}`);
        res.status(500).send('服务器错误');
    }
});

// 下载Markdown文件
app.get('/download/:filename', (req, res) => {
    const filename = req.params.filename;
    const filepath = path.join('uploads', filename);
    
    logger.info(`请求下载文件: ${filename}`);
    
    if (!fs.existsSync(filepath)) {
        logger.warn(`文件不存在: ${filepath}`);
        return res.status(404).send('文件不存在或已过期');
    }
    
    try {
        logger.info(`开始下载文件: ${filename}`);
        res.download(filepath, filename, (err) => {
            if (err) {
                logger.error(`下载文件失败: ${err.message}`);
                return res.status(500).send('下载失败');
            }
            
            // 下载完成后删除文件（可选）
            // fs.unlinkSync(filepath);
        });
    } catch (error) {
        logger.error(`文件下载异常: ${error.message}`);
        res.status(500).send('服务器错误');
    }
});

// 添加新的直接调用路由
app.post('/api/review', async (req, res) => {
    try {
        const { content, prompt_template } = req.body;
        
        if (!content) {
            return res.status(400).json({
                error: '请求参数无效：缺少 content'
            });
        }
        
        // 检查是否需要按章节审核
        const chapterContent = prompt_template ? getChapterContent(content, prompt_template) : content;
        
        // 调用 DeepSeek API 进行文档审核
        const result = await deepseekService.reviewDocument(chapterContent, prompt_template);
        
        // 返回审核结果
        res.json({
            result: result.result,
            thinking: result.thinking
        });
        
    } catch (error) {
        console.error('处理审核请求失败:', error);
        res.status(500).json({
            error: `处理审核请求失败: ${error.message}`
        });
    }
});

// 处理Excel生成Prompt
app.post('/process-excel', async (req, res) => {
    try {
        const { tempPath, filename, modelType = 'deepseek-reasoner' } = req.body;
        
        if (!tempPath || !filename || !fs.existsSync(tempPath)) {
            return res.status(400).json({ error: '文件不存在或已过期，请重新上传' });
        }
        
        logger.info(`开始处理Excel文件: ${filename}, 模型: ${modelType}`);
        
        // 创建任务ID，方便日志跟踪
        const taskId = Date.now().toString();
        const logQueue = [];
        const addLog = (type, message) => {
            const timestamp = new Date().toISOString().replace('T', ' ').substr(0, 19);
            const logMessage = `${timestamp} [${type.toUpperCase()}]: ${message}`;
            logQueue.push(logMessage);
            
            // 使用正确的logger方法
            const logMethod = type.toLowerCase();
            if (typeof logger[logMethod] === 'function') {
                logger[logMethod](message);
            } else {
                logger.info(`[${type}] ${message}`); // 兜底处理
            }
        };
        
        // 读取Excel文件
        const workbook = xlsx.readFile(tempPath);
        
        // 获取第一个未隐藏的工作表
        const sheetName = getFirstVisibleSheet(workbook);
        
        // 检查工作表是否存在
        if (!workbook.SheetNames.includes(sheetName)) {
            return res.status(400).json({ error: `Excel文件中未找到工作表"${sheetName}"` });
        }
        
        // 读取工作表数据
        const worksheet = workbook.Sheets[sheetName];
        const data = xlsx.utils.sheet_to_json(worksheet);
        
        if (data.length === 0) {
            addLog('WARN', 'Excel文件中未找到有效数据');
            return res.status(400).json({ error: 'Excel文件中未找到有效数据' });
        }
        
        addLog('INFO', `从Excel中读取到 ${data.length} 行数据，开始生成提示词`);
        
        // 构建提示词生成的提示模板
        const buildPromptTemplate = (category, item) => {
            return `
请参考【少量样本】，将【审核要求】:
"审查检查类别:"${category}"。
审查检查项:${item}"
改写为【审核要求提示词】。

输出要求：
-只输出审核要求提示词
-不要说其他内容
-紧贴审查检查项的要求
-要求不能太严格，尽量给出宽松的审核要求
-去掉{}。

【少量样本】：
example1：
【审核要求】：
{
审查检查类别：配置管理。
审查检查项：日志配置有变化(名称、路径、格式、级别、数据量、空间使用、清理、备份等)，该变化对系统功能是否有影响。
}
审核要求提示词:
{
##角色:你是一名软件技术方案与详细设计审核专家。
##任务：根据给定要求对文段内容进行判断
##要求：
1、主要根据审核内容，判断是否满足【触发审核条件】
2、章节内容不满足条件时，直接给出【审核结论】为"不涉及"，【审核理由】为"无"；
3、章节内容满足条件时，按照【检查标准】对审核内容进行检查，若所有条件满足标准，则【审核结论】为"通过"，【审核理由】为"无"，
4、若任何一条标准不满足，则【审核结论】为"不通过"，【审核理由】列出所有不满足的标准。

##【触发审核条件】:
当且仅当审核内容中有日志配置有变化，包括但不限于名称、路径、格式、级别、数据量、空间使用、清理、备份。

##【检查标准】:
1、审核内容需包括该变化对系统功能是否有影响的分析。

##输出要求:
【审核结论】：不涉及/不通过/通过
【审核理由】:输出不满足的【检查标准】及原文描述。格式一般为原文提及"xxx",但xxxxx，违反了【xxx】。满足或不涉及时，原因写"无"。

##输出示例1:
结论:不通过。
原因:原文提及"循环插入数据"但未在后文提及记录行数的相关操作,违反了"如有大量insert语句导致数据量在100万以上的表产生大量数据变化，应包括insert语句预估数据插入后表空间的增长情况的设计"的要求
##输出示例2:
结论:通过。
原因:无
##【审核章节】：配置管理
}

example2：
excel某行内容（内容用{}括起来）：
{
审查检查类别：数据库设计。
审查检查项：大数据库表（500万记录以上）有增/删/改数据操作时，a) 如有大量insert语句导致数据量在100万以上的表产生大量数据变化，应根据insert语句预估数据插入后表空间的增长情况；b)如果sql涉及插入、更新中文字符，确保变更脚本的文件格式是UTF-8 UNIX无BOM格式，以便防止乱码出现
}
生成对应Prompt:
{
##角色:你是一名软件技术方案与详细设计审核专家。
##任务：根据给定要求对文段内容进行判断
##要求：
1、主要根据审核内容，判断是否满足【触发审核条件】
2、章节内容不满足条件时，直接给出【审核结论】为"不涉及"，【审核理由】为"无"；
3、章节内容满足条件时，按照【检查标准】对审核内容进行检查，若所有条件满足标准，则【审核结论】为"通过"，【审核理由】为"无"，
4、若任何一条标准不满足，则【审核结论】为"不通过"，【审核理由】列出所有不满足的标准。

##【触发审核条件】:
当且仅当审核内容中有描述表记录数≥500万时触发。

##【检查标准】:
1、如有大量insert语句导致数据量在100万以上的表产生大量数据变化，应包括insert语句预估数据插入后表空间的增长情况的设计。
2、如果sql涉及插入、更新中文字符，确保变更脚本的文件格式是UTF-8 UNIX无BOM格式，以便防止乱码出现

##输出要求:
【审核结论】：不涉及/不通过/通过
【审核理由】:输出不满足的【检查标准】及原文描述。格式一般为原文提及"xxx",但xxxxx，违反了【xxx】。满足或不涉及时，原因写"无"。

##输出示例1:
结论:不通过。
原因:原文提及"循环插入数据"但未在后文提及记录行数的相关操作,违反了"如有大量insert语句导致数据量在100万以上的表产生大量数据变化，应包括insert语句预估数据插入后表空间的增长情况的设计"的要求
##输出示例2:
结论:通过。
原因:无
##【审核章节】：数据库设计
}
`;
        };

        // 定义调用模型的函数
        async function callModelToGeneratePrompt(category, item) {
            try {
                if (!category || !item) {
                    return { 
                        prompt: null, 
                        error: '缺少审查检查类别或检查项' 
                    };
                }
                
                const promptTemplate = buildPromptTemplate(category, item);
                
                if (modelType === 'deepseek-reasoner') {
                    // 使用DeepSeek模型
                    const response = await deepseekService.chatCompletion([
                        { role: "user", content: promptTemplate }
                    ]);
                    
                    // 提取生成的提示词，移除可能的包装符号
                    let generatedPrompt = response.content || '';
                    
                    // 尝试提取大括号中的内容
                    const bracketMatch = generatedPrompt.match(/{([^}]+)}/s);
                    if (bracketMatch) {
                        generatedPrompt = bracketMatch[1].trim();
                    }
                    
                    // 检查并清理生成的提示词
                    generatedPrompt = generatedPrompt.replace(/^```(json)?|```$/g, '').trim();
                    
                    return { 
                        prompt: generatedPrompt, 
                        error: null 
                    };
                } else {
                    // 使用CCB方舟模型
                    const response = await ccbfzService.reviewDocument(
                        '', 
                        promptTemplate, 
                        modelType
                    );
                    
                    // 提取生成的提示词
                    let generatedPrompt = response.result || '';
                    
                    // 尝试提取大括号中的内容
                    const bracketMatch = generatedPrompt.match(/{([^}]+)}/s);
                    if (bracketMatch) {
                        generatedPrompt = bracketMatch[1].trim();
                    }
                    
                    // 检查并清理生成的提示词
                    generatedPrompt = generatedPrompt.replace(/^```(json)?|```$/g, '').trim();
                    
                    return { 
                        prompt: generatedPrompt, 
                        error: null 
                    };
                }
            } catch (error) {
                console.error(`生成提示词失败: ${error.message}`);
                return { 
                    prompt: null, 
                    error: `生成提示词失败: ${error.message}` 
                };
            }
        }
        
        // 设置初始响应头，允许流式传输数据
        res.writeHead(200, {
            'Content-Type': 'application/json',
            'Transfer-Encoding': 'chunked'
        });
        
        // 发送初始状态信息
        res.write(JSON.stringify({
            status: 'processing',
            total: data.length,
            completed: 0,
            logs: logQueue
        }));
        
        // 准备批次处理数据
        const batchSize = parseInt(process.env.MODEL_BATCH_SIZE) || 9; // 从环境变量获取批次大小，默认为3
        const processedData = [...data]; // 复制原始数据
        let completed = 0;
        
        // 创建临时数组存储所有批处理结果和原始索引，方便后续排序
        const batchResults = [];
        
        // 分批处理数据
        for (let i = 0; i < data.length; i += batchSize) {
            const batch = data.slice(i, i + batchSize);
            const batchTasks = batch.map(async (row, batchIndex) => {
                const index = i + batchIndex;
                const category = row['审查检查类别'] || '';
                const item = row['审查检查项'] || '';
                
                if (!category || !item) {
                    addLog('WARN', `第 ${index + 1} 行缺少审查检查类别或检查项，跳过处理`);
                    return { 
                        index,
                        row: { ...row, prompt: null, error: '缺少审查检查类别或检查项' }
                    };
                }
                
                addLog('INFO', `开始为第 ${index + 1} 行生成提示词: 类别=${category}`);
                
                try {
                    const result = await callModelToGeneratePrompt(category, item);
                    
                    if (result.error) {
                        addLog('ERROR', `第 ${index + 1} 行提示词生成失败: ${result.error}`);
                        return { 
                            index,
                            row: { ...row, prompt: null, error: result.error }
                        };
                    } else {
                        addLog('SUCCESS', `第 ${index + 1} 行提示词生成成功`);
                        return { 
                            index,
                            row: { ...row, prompt: result.prompt, error: null }
                        };
                    }
                } catch (error) {
                    addLog('ERROR', `处理第 ${index + 1} 行时发生错误: ${error.message}`);
                    return { 
                        index,
                        row: { ...row, prompt: null, error: error.message }
                    };
                }
            });
            
            // 等待当前批次完成，获取结果并添加到临时数组
            const currentBatchResults = await Promise.all(batchTasks);
            batchResults.push(...currentBatchResults);
            
            // 更新进度
            completed += batch.length;
            const percent = Math.round((completed / data.length) * 100);
            
            // 发送进度更新
            res.write(JSON.stringify({
                status: 'processing',
                total: data.length,
                completed: completed,
                percent: percent,
                logs: logQueue
            }));
            
            // 清空日志队列，避免重复发送
            logQueue.length = 0;
            
            // 短暂延迟，允许客户端处理更新
            await new Promise(resolve => setTimeout(resolve, 200));
        }
        
        // 处理完所有批次后，对结果按原始索引排序
        addLog('INFO', '所有批次处理完成，正在对结果按原始顺序排序...');
        batchResults.sort((a, b) => a.index - b.index);
        
        // 按顺序将排序后的结果更新到processedData数组
        for (const result of batchResults) {
            processedData[result.index] = result.row;
        }
        
        addLog('INFO', '结果排序完成，确保保持原始Excel表行顺序');
        
        // 统计成功和失败的行数
        const successCount = processedData.filter(row => row.prompt).length;
        const failCount = processedData.filter(row => row.error).length;
        addLog('INFO', `处理结果统计: 成功=${successCount}, 失败=${failCount}`);
        
        // 清理数据中的error字段，避免在Excel中出现error列
        processedData.forEach(row => {
            delete row.error;
        });
        
        // 将更新后的数据写回工作表
        const newWorkbook = xlsx.utils.book_new();
        const newWorksheet = xlsx.utils.json_to_sheet(processedData);
        xlsx.utils.book_append_sheet(newWorkbook, newWorksheet, sheetName);
        
        // 保存到新文件 - 使用英文名称避免乱码问题
        const timestamp = new Date().getTime();
        const outputFilename = `excel_prompts_${timestamp}.xlsx`;
        const outputPath = path.join('uploads', outputFilename);
        xlsx.writeFile(newWorkbook, outputPath);
        addLog('INFO', `结果文件已生成: ${outputPath}`);
        
        // 发送最终结果
        res.end(JSON.stringify({
            success: true,
            status: 'completed',
            message: `成功为${successCount}行生成prompt，${failCount}行失败`,
            download_path: '/download-excel/' + outputFilename,
            logs: logQueue
        }));
    } catch (error) {
        logger.error(`处理Excel文件失败: ${error.message}`);
        res.end(JSON.stringify({ 
            status: 'failed',
            error: '处理Excel文件失败: ' + error.message 
        }));
    }
});

// 文档完整性检查API路由
app.post('/doc-completeness-check', upload.single('wordFile'), async (req, res) => {
    // 开始记录日志队列
    const taskId = Date.now().toString();
    const logQueue = [];
    const addLog = (type, message) => {
        const timestamp = new Date().toISOString().replace('T', ' ').substr(0, 19);
        const logMessage = `${timestamp} [${type.toUpperCase()}]: ${message}`;
        logQueue.push(logMessage);
        
        // 使用正确的logger方法
        const logMethod = type.toLowerCase();
        if (typeof logger[logMethod] === 'function') {
            logger[logMethod](message);
        } else {
            logger.info(`[${type}] ${message}`); // 兜底处理
        }
        
        // 存储日志到taskLogs对象
        if (!taskLogs[taskId]) {
            taskLogs[taskId] = [];
        }
        taskLogs[taskId].push(logMessage);
    };

    if (!req.file) {
        addLog('WARN', '没有文件上传到文档完整性检查');
        return res.status(400).json({ error: '请上传Word文档' });
    }

    try {
        // 解码文件名，避免中文乱码
        const decodeFileName = (fileName) => {
            try {
                return Buffer.from(fileName, 'latin1').toString('utf8');
            } catch (e) {
                return fileName;
            }
        };
        
        const originalFileName = decodeFileName(req.file.originalname);
        const fileExtension = path.extname(originalFileName).toLowerCase();
        const docType = req.body.docType || '未指定类型';
        const modelType = req.body.modelType || 'deepseek-reasoner';
        
        addLog('INFO', `文档完整性检查: ${originalFileName}，类型: ${fileExtension}，文档分类: ${docType}，模型: ${modelType}`);
        
        if (fileExtension !== '.docx') {
            addLog('WARN', `不支持的文件类型: ${fileExtension}，应为.docx`);
            return res.status(400).json({ error: '请上传Word文档(docx格式)' });
        }

        // 使用mammoth.convertToMarkdown将Word转换为Markdown文本
        const options = {
            convertImage: () => ({ 
                type: "text",
                value: "图片"
            }),
            idGenerator: {
                generate: () => "" // 返回空字符串，禁用 ID 生成
            },
            transformDocument: (element) => {
                if (element.type === "hyperlink" && 
                    element.destination?.toLowerCase().includes("_toc")) {
                    return { 
                        type: "text", 
                        value: element.children[0].value
                    };
                }
                return element; // 其他元素保持原样
            }
        };
        
        addLog('INFO', '开始将Word文档转换为Markdown...');
        const wordResult = await mammoth.convertToMarkdown({ path: req.file.path }, options);
        const markdownContent = wordResult.value.replace(/($$.*?$$)\s*$#_Toc\d+$/g, "$1").replace(/_Toc\d+/g, "");
        addLog('INFO', `文档转换为Markdown成功，内容长度: ${markdownContent.length}，已忽略图片`);

        // 构建两个不同的提示词
        addLog('INFO', '构建章节和标签提示词...');
        const sectionPrompt = buildSectionCheckPrompt(markdownContent, docType);
        const tagPrompt = buildTagCheckPrompt(markdownContent, docType);
        
        // 记录请求
        addLog('INFO', `API调用信息: 模型=${modelType}, 章节提示词长度=${sectionPrompt.length}, 标签提示词长度=${tagPrompt.length}`);
        
        try {
            // 并行调用大模型分析
            addLog('INFO', `开始并行调用大模型分析章节和标签...`);
            let sectionResult, tagResult;
            
            // 定义调用函数
            async function callModel(prompt, type) {
                if (modelType === 'deepseek-reasoner') {
                    addLog('INFO', `使用DeepSeek R1模型分析${type}`);
                    const response = await deepseekService.chatCompletion([
                        { role: "user", content: prompt }
                    ]);
                    return response.content || "分析失败";
                } else {
                    addLog('INFO', `使用CCB方舟模型分析${type}: ${modelType}`);
                    const response = await ccbfzService.reviewDocument(markdownContent, prompt, modelType);
                    return response.result || "分析失败";
                }
            }
            
            // 解析JSON结果
            function parseJsonResult(content) {
                try {
                    const jsonMatch = content.match(/```json\n([\s\S]*?)\n```/) || 
                                    content.match(/```\n([\s\S]*?)\n```/) ||
                                    content.match(/{[\s\S]*}/);
                    
                    const jsonContent = jsonMatch ? jsonMatch[1] || jsonMatch[0] : content;
                    return JSON.parse(jsonContent);
    } catch (error) {
                    throw new Error(`解析结果失败: ${error.message}`);
                }
            }
            
            // 并行调用
            const [sectionContent, tagContent] = await Promise.all([
                callModel(sectionPrompt, '章节完整性'),
                callModel(tagPrompt, '标签识别')
            ]);
            
            // 解析结果
            addLog('INFO', '解析章节分析结果...');
            sectionResult = parseJsonResult(sectionContent);
            addLog('INFO', '成功解析章节完整性分析结果');
            
            addLog('INFO', '解析标签分析结果...');
            tagResult = parseJsonResult(tagContent);
            addLog('INFO', '成功解析标签识别分析结果');
            
            // 合并结果
            addLog('INFO', '合并分析结果...');
            const parsedResult = mergeResults(sectionResult, tagResult);
            addLog('INFO', '成功合并分析结果');
            
            // 生成HTML报告
            addLog('INFO', '生成HTML报告...');
            const htmlReport = await generateDocCheckHtmlReport(parsedResult, originalFileName, docType);
            
            // 保存HTML报告
            const timestamp = new Date().getTime();
            const htmlFilename = `${path.basename(originalFileName, '.docx')}_完整性报告_${timestamp}.html`;
            const htmlOutputPath = path.join('uploads', htmlFilename);
            fs.writeFileSync(htmlOutputPath, htmlReport);
            addLog('INFO', `HTML报告生成成功: ${htmlOutputPath}`);
            
            // 添加成功日志
            addLog('SUCCESS', '文档完整性检查完成');
            
            // 返回结果、日志和下载路径
            res.json({
                ...parsedResult,
                docType: docType,
                logs: logQueue,
                taskId: taskId,
                originalFileName: originalFileName,
                htmlReport: `/download/${htmlFilename}`,
                htmlContent: htmlReport  // 直接在响应中包含HTML内容方便页面渲染
            });
        } catch (error) {
            addLog('ERROR', `调用模型分析或解析结果失败: ${error.message}`);
            return res.status(500).json({ 
                error: `分析失败: ${error.message}`,
                logs: logQueue,
                taskId: taskId
            });
        }
    } catch (error) {
        addLog('ERROR', `文档完整性检查错误: ${error.message}`);
        res.status(500).json({ 
            error: '处理文档完整性检查时出错: ' + error.message,
            logs: logQueue,
            taskId: taskId
        });
    }
});

// 生成HTML格式的文档完整性检查报告
async function generateDocCheckHtmlReport(result, originalFileName, docType) {
    try {
        // 获取模板
        const templatePath = path.join(__dirname, 'template/doccheck_report.html');
        let template = fs.readFileSync(templatePath, 'utf8');
        
        // 检查结果数据的有效性
        if (!result) {
            logger.error('结果对象为空，无法生成HTML报告');
            result = {
                sections: [],
                tags: [],
                summary: '分析结果出错，无法生成完整报告'
            };
        }
        
        // 确保sections和tags是数组
        const sections = Array.isArray(result.sections) ? result.sections : [];
        const tags = Array.isArray(result.tags) ? result.tags : [];
        const summary = result.summary || '未提供总结';
        const totalstatus = result.totalstatus || '未评估';
        
        if (sections.length === 0) {
            logger.warn('sections为空或格式不正确');
        }
        
        if (tags.length === 0) {
            logger.warn('tags为空或格式不正确');
        }
        
        // 图标字符映射
        const statusIcons = {
            'complete': '✓',
            'incomplete': '⚠',
            'missing': '✗'
        };
        
        // 为状态添加CSS类
        let totalStatusClass = '';
        if (totalstatus.includes('良好') || totalstatus.includes('优')) {
            totalStatusClass = 'score-high';
        } else if (totalstatus.includes('中等') || totalstatus.includes('一般')) {
            totalStatusClass = 'score-medium';
        } else {
            totalStatusClass = 'score-low';
        }
        
        // 准备渲染数据
        const processedSections = sections.map(section => {
            return {
                ...section,
                statusIcon: statusIcons[section.status] || '?',
                isMissing: section.status === 'missing'
            };
        });
        
        const processedTags = tags.map(tag => {
            return {
                ...tag,
                relatedText: tag.related ? '是' : '否'
            };
        });
        
        // 简单模板替换
        const date = new Date().toLocaleString();
        
        // 替换基础信息
        template = template.replace('{{originalFileName}}', originalFileName);
        template = template.replace('{{docType}}', docType);
        template = template.replace('{{date}}', date);
        template = template.replace('{{summary}}', summary);
        template = template.replace('{{totalstatus}}', totalstatus);
        template = template.replace('{{totalStatusClass}}', totalStatusClass);
        
        // 替换章节数据
        let sectionsHtml = '';
        if (processedSections.length > 0) {
            processedSections.forEach(section => {
                const missingClass = section.isMissing ? 'class="missing-row"' : '';
                sectionsHtml += `
                <tr ${missingClass}>
                    <td>
                        <span class="status-${section.status}">
                            <span class="status-icon">${section.statusIcon}</span>
                        </span>
                    </td>
                    <td>${section.name || '未命名章节'}</td>
                    <td>${section.description || ''}</td>
                </tr>`;
            });
        } else {
            sectionsHtml = `<tr>
                <td colspan="3" style="text-align: center;">未检测到章节信息或数据格式不正确</td>
            </tr>`;
        }
        template = template.replace('{{#sections}}' + 
            template.substring(template.indexOf('{{#sections}}') + 13, template.indexOf('{{/sections}}')) + 
            '{{/sections}}', sectionsHtml);
        template = template.replace('{{^sections}}' + 
            template.substring(template.indexOf('{{^sections}}') + 13, template.indexOf('{{/sections}}', template.indexOf('{{^sections}}'))) + 
            '{{/sections}}', '');
        
        // 替换标签数据
        let tagsHtml = '';
        if (processedTags.length > 0) {
            processedTags.forEach(tag => {
                const relatedClass = tag.related ? 'class="related-row"' : '';
                const relatedStyle = tag.related ? 'color: #6F42C1; font-weight: bold;' : 'color: #666;';
                tagsHtml += `
                <tr ${relatedClass}>
                    <td>${tag.name || '未命名标签'}</td>
                    <td>
                        <span style="${relatedStyle}">
                            ${tag.relatedText}
                        </span>
                    </td>
                    <td>${tag.evidence || ''}</td>
                </tr>`;
            });
        } else {
            tagsHtml = `<tr>
                <td colspan="3" style="text-align: center;">未检测到标签信息或数据格式不正确</td>
            </tr>`;
        }
        template = template.replace('{{#tags}}' + 
            template.substring(template.indexOf('{{#tags}}') + 9, template.indexOf('{{/tags}}')) + 
            '{{/tags}}', tagsHtml);
        template = template.replace('{{^tags}}' + 
            template.substring(template.indexOf('{{^tags}}') + 9, template.indexOf('{{/tags}}', template.indexOf('{{^tags}}'))) + 
            '{{/tags}}', '');
        
        return template;
    } catch (error) {
        logger.error(`生成HTML报告失败: ${error.message}`);
        return `<div class="error">生成报告失败: ${error.message}</div>`;
    }
}

// 添加日志获取路由
app.get('/doc-check-logs/:taskId', (req, res) => {
    const taskId = req.params.taskId;
    // 实际实现中这里应该从数据库或内存中获取指定taskId的日志
    // 这里只是一个示例，实际的taskLogs应该有持久化的存储
    const logs = taskLogs[taskId] || [];
    res.json({ logs });
});

// 生成Word格式的文档完整性检查报告
async function generateDocCheckReport(result, outputPath, originalFileName, docType) {
    try {
        // 使用docx库生成Word文档
        const docx = require('docx');
        const { Document, Paragraph, TextRun, Table, TableRow, TableCell, BorderStyle, AlignmentType, HeadingLevel } = docx;
        
        // 检查result结构，确保关键字段存在
        if (!result) {
            logger.error('结果对象为空，无法生成报告');
            result = {
                sections: [],
                tags: [],
                summary: '分析结果出错，无法生成完整报告'
            };
        }
        
        // 确保sections和tags是数组
        const sections = Array.isArray(result.sections) ? result.sections : [];
        const tags = Array.isArray(result.tags) ? result.tags : [];
        const summary = result.summary || '未提供总结';
        
        if (sections.length === 0) {
            logger.warn('sections为空或格式不正确');
        }
        
        if (tags.length === 0) {
            logger.warn('tags为空或格式不正确');
        }
        
        // 图标字符映射
        const statusIcons = {
            'complete': '✓',
            'incomplete': '⚠',
            'missing': '✗'
        };
        
        // 创建文档
        const doc = new Document({
            styles: {
                paragraphStyles: [
                    {
                        id: "Heading1",
                        name: "Heading 1",
                        run: {
                            size: 36,
                            bold: true,
                            color: "6F42C1",
                        },
                        paragraph: {
                            spacing: {
                                after: 200,
                            },
                        },
                    },
                    {
                        id: "Heading2",
                        name: "Heading 2",
                        run: {
                            size: 28,
                            bold: true,
                            color: "444444",
                        },
                        paragraph: {
                            spacing: {
                                before: 240,
                                after: 120,
                            },
                        },
                    },
                ]
            }
        });
        
        // 添加标题和基本信息
        const children = [
            new Paragraph({
                text: "文档完整性检查报告",
                heading: HeadingLevel.HEADING_1,
                alignment: AlignmentType.CENTER,
            }),
            new Paragraph({
                text: `文件名: ${originalFileName}`,
                alignment: AlignmentType.LEFT,
            }),
            new Paragraph({
                text: `文档类型: ${docType}`,
                alignment: AlignmentType.LEFT,
            }),
            new Paragraph({
                text: `生成日期: ${new Date().toLocaleString()}`,
                alignment: AlignmentType.LEFT,
            }),
            new Paragraph({
                text: "",
            }),
            new Paragraph({
                text: "章节完整性检查",
                heading: HeadingLevel.HEADING_2,
            }),
        ];
        
        // 创建章节完整性表格
        const sectionRows = [
            new TableRow({
                tableHeader: true,
                children: [
                    new TableCell({
                        width: {
                            size: 1000,
                            type: "dxa",
                        },
                        children: [new Paragraph("状态")],
                        shading: {
                            fill: "F3F3F3",
                        },
                    }),
                    new TableCell({
                        width: {
                            size: 3000,
                            type: "dxa",
                        },
                        children: [new Paragraph("章节名称")],
                        shading: {
                            fill: "F3F3F3",
                        },
                    }),
                    new TableCell({
                        width: {
                            size: 4000,
                            type: "dxa",
                        },
                        children: [new Paragraph("完整性评分")],
                        shading: {
                            fill: "F3F3F3",
                        },
                    }),
                    new TableCell({
                        width: {
                            size: 4000,
                            type: "dxa",
                        },
                        children: [new Paragraph("描述")],
                        shading: {
                            fill: "F3F3F3",
                        },
                    }),
                ],
            }),
        ];
        
        // 安全地添加章节行
        if (Array.isArray(sections)) {
            for (const section of sections) {
                if (!section) continue; // 跳过无效条目
                
                const icon = statusIcons[section.status] || '?';
                let iconColor = "#000000";
                let rowShading = null;
                
                // 根据状态设置颜色
                switch(section.status) {
                    case 'complete':
                        iconColor = "#28A745"; // 绿色
                        break;
                    case 'incomplete':
                        iconColor = "#FFC107"; // 黄色
                        break;
                    case 'missing':
                        iconColor = "#DC3545"; // 红色
                        rowShading = "FFF0F0"; // 浅红色背景
                        break;
                }
                
                sectionRows.push(
                    new TableRow({
                        children: [
                            new TableCell({
                                children: [
                                    new Paragraph({
                                        children: [
                                            new TextRun({
                                                text: icon,
                                                bold: true,
                                                size: 24,
                                                color: iconColor,
                                            }),
                                        ],
                                        alignment: AlignmentType.CENTER,
                                    }),
                                ],
                                shading: rowShading ? { fill: rowShading } : undefined,
                            }),
                            new TableCell({
                                children: [
                                    new Paragraph({
                                        text: section.name || "未命名章节",
                                        alignment: AlignmentType.LEFT,
                                    }),
                                ],
                                shading: rowShading ? { fill: rowShading } : undefined,
                            }),
                            new TableCell({
                                children: [
                                    new Paragraph({
                                        children: [
                                            new TextRun({
                                                text: section.score || "-",
                                                bold: section.score ? true : false,
                                            }),
                                        ],
                                        alignment: AlignmentType.CENTER,
                                    }),
                                ],
                                shading: rowShading ? { fill: rowShading } : undefined,
                            }),
                            new TableCell({
                                children: [
                                    new Paragraph({
                                        text: section.description || "",
                                        alignment: AlignmentType.LEFT,
                                    }),
                                ],
                                shading: rowShading ? { fill: rowShading } : undefined,
                            }),
                        ],
                    })
                );
            }
        }
        
        // 如果没有章节数据，添加一个提示行
        if (sectionRows.length <= 1) {
            sectionRows.push(
                new TableRow({
                    children: [
                        new TableCell({
                            columnSpan: 4,
                            children: [
                                new Paragraph({
                                    text: "未检测到章节信息或数据格式不正确",
                                    alignment: AlignmentType.CENTER,
                                }),
                            ],
                        }),
                    ],
                })
            );
        }
        
        const sectionTable = new Table({
            rows: sectionRows,
            width: {
                size: 100,
                type: "pct",
            },
            borders: {
                top: { style: BorderStyle.SINGLE, size: 1, color: "CCCCCC" },
                bottom: { style: BorderStyle.SINGLE, size: 1, color: "CCCCCC" },
                left: { style: BorderStyle.SINGLE, size: 1, color: "CCCCCC" },
                right: { style: BorderStyle.SINGLE, size: 1, color: "CCCCCC" },
                insideHorizontal: { style: BorderStyle.SINGLE, size: 1, color: "CCCCCC" },
                insideVertical: { style: BorderStyle.SINGLE, size: 1, color: "CCCCCC" },
            },
        });
        
        children.push(sectionTable);
        
        // 添加标签识别部分
        children.push(
            new Paragraph({
                text: "",
            }),
            new Paragraph({
                text: "关注标签识别",
                heading: HeadingLevel.HEADING_2,
            })
        );
        
        // 创建标签表格
        const tagRows = [
            new TableRow({
                tableHeader: true,
                children: [
                    new TableCell({
                        width: {
                            size: 3000,
                            type: "dxa",
                        },
                        children: [new Paragraph("标签名称")],
                        shading: {
                            fill: "F3F3F3",
                        },
                    }),
                    new TableCell({
                        width: {
                            size: 2000,
                            type: "dxa",
                        },
                        children: [new Paragraph("相关性")],
                        shading: {
                            fill: "F3F3F3",
                        },
                    }),
                    new TableCell({
                        width: {
                            size: 4000,
                            type: "dxa",
                        },
                        children: [new Paragraph("判断依据")],
                        shading: {
                            fill: "F3F3F3",
                        },
                    }),
                ],
            }),
        ];
        
        // 安全地添加标签行
        if (Array.isArray(tags)) {
            for (const tag of tags) {
                if (!tag) continue; // 跳过无效条目
                
                const isRelated = tag.related ? "是" : "否";
                const rowShading = tag.related ? "F2E9FB" : null; // 相关标签使用浅紫色背景
                
                tagRows.push(
                    new TableRow({
                        children: [
                            new TableCell({
                                children: [
                                    new Paragraph({
                                        text: tag.name || "未命名标签",
                                        alignment: AlignmentType.LEFT,
                                    }),
                                ],
                                shading: rowShading ? { fill: rowShading } : undefined,
                            }),
                            new TableCell({
                                children: [
                                    new Paragraph({
                                        children: [
                                            new TextRun({
                                                text: isRelated,
                                                bold: tag.related,
                                                color: tag.related ? "6F42C1" : "666666",
                                            }),
                                        ],
                                        alignment: AlignmentType.CENTER,
                                    }),
                                ],
                                shading: rowShading ? { fill: rowShading } : undefined,
                            }),
                            new TableCell({
                                children: [
                                    new Paragraph({
                                        text: tag.evidence || "",
                                        alignment: AlignmentType.LEFT,
                                    }),
                                ],
                                shading: rowShading ? { fill: rowShading } : undefined,
                            }),
                        ],
                    })
                );
            }
        }
        
        // 如果没有标签数据，添加一个提示行
        if (tagRows.length <= 1) {
            tagRows.push(
                new TableRow({
                    children: [
                        new TableCell({
                            columnSpan: 3,
                            children: [
                                new Paragraph({
                                    text: "未检测到标签信息或数据格式不正确",
                                    alignment: AlignmentType.CENTER,
                                }),
                            ],
                        }),
                    ],
                })
            );
        }
        
        const tagTable = new Table({
            rows: tagRows,
            width: {
                size: 100,
                type: "pct",
            },
            borders: {
                top: { style: BorderStyle.SINGLE, size: 1, color: "CCCCCC" },
                bottom: { style: BorderStyle.SINGLE, size: 1, color: "CCCCCC" },
                left: { style: BorderStyle.SINGLE, size: 1, color: "CCCCCC" },
                right: { style: BorderStyle.SINGLE, size: 1, color: "CCCCCC" },
                insideHorizontal: { style: BorderStyle.SINGLE, size: 1, color: "CCCCCC" },
                insideVertical: { style: BorderStyle.SINGLE, size: 1, color: "CCCCCC" },
            },
        });
        
        children.push(tagTable);
        
        // 添加总结
        children.push(
            new Paragraph({
                text: "",
            }),
            new Paragraph({
                text: "总结",
                heading: HeadingLevel.HEADING_2,
            }),
            new Paragraph({
                text: summary,
                alignment: AlignmentType.LEFT,
            })
        );
        
        // 将所有内容添加到文档中
        doc.addSection({
            children: children,
        });
        
        // 生成Word文档并正确等待Promise完成
        const buffer = await docx.Packer.toBuffer(doc);
        fs.writeFileSync(outputPath, buffer);
        logger.info(`生成Word报告成功: ${outputPath}`);
        
    } catch (error) {
        logger.error(`生成Word报告失败: ${error.message}`);
        throw error;
    }
}

// 服务器端口
const PORT = process.env.PORT || 3001;

// 启动服务器
const server = app.listen(PORT,'0.0.0.0', () => {
    logger.info(`======= Web服务启动 =======`);
    logger.info(`服务器运行在 http://localhost:${PORT}`);
    logger.info(`日志保存在 ${path.join(__dirname, 'log')} 目录下`);
    console.log(`服务器运行在 http://localhost:${PORT}`);
});

// 处理服务器错误
server.on('error', (error) => {
    logger.error(`服务器错误: ${error.message}`);
    console.error('服务器错误:', error);
});

// 处理进程退出事件
process.on('SIGTERM', () => {
    logger.info('接收到SIGTERM信号，关闭服务器');
    server.close(() => {
        logger.info('服务器已关闭');
        process.exit(0);
    });
});

process.on('SIGINT', () => {
    logger.info('接收到SIGINT信号，关闭服务器');
    server.close(() => {
        logger.info('服务器已关闭');
        process.exit(0);
    });
});

// 文档模板套用相关路由和处理函数
const templateTasks = {}; // 存储模板套用任务状态
const templatePath = path.join(__dirname, 'template', 'designdoctemp');

// 文档模板套用处理入口
app.post('/process-template', upload.fields([
    { name: 'docFile', maxCount: 1 }
]), async (req, res) => {
    try {
        if (!req.files || !req.files.docFile || !req.files.docFile[0]) {
            return res.status(400).json({ error: '请上传Word文档' });
        }

        const docFile = req.files.docFile[0];
        const templateType = req.body.templateType || '产品服务类';
        const modelType = req.body.modelType || 'DeepSeek-R1-QW32B_SFT_0228_V1';
        
        // 生成任务ID
        const taskId = uuidv4();
        
        // 创建上传文件的临时路径
        const uploadedFilePath = path.join(tempDir, docFile.filename);
        fs.renameSync(docFile.path, uploadedFilePath);
        
        // 获取模板文件路径
        const templateFiles = fs.readdirSync(templatePath);
        let templateFilePath = '';
        
        for (const file of templateFiles) {
            if (file.includes(templateType)) {
                templateFilePath = path.join(templatePath, file);
                break;
            }
        }
        
        if (!templateFilePath) {
            return res.status(400).json({ error: `未找到${templateType}对应的模板文件` });
        }
        
        // 初始化任务状态
        templateTasks[taskId] = {
            status: 'pending',
            uploadedFilePath,
            templateFilePath,
            templateType,
            modelType,
            logs: [],
            completed: 0,
            total: 15, // 章节总数（包括子章节）
            chapterStatus: {},
            startTime: new Date(),
            error: null,
            downloadPath: null
        };
        
        // 异步处理任务
        processTemplateTask(taskId).catch(err => {
            console.error(`Template task ${taskId} failed:`, err);
            templateTasks[taskId].status = 'failed';
            templateTasks[taskId].error = err.message;
            templateTasks[taskId].logs.push(`[ERROR] 模板套用失败: ${err.message}`);
        });
        
        // 立即返回任务ID
        res.json({ 
            success: true, 
            taskId,
            message: '模板套用任务已开始，请等待完成'
        });
    } catch (error) {
        console.error('处理模板套用请求出错:', error);
        res.status(500).json({ error: `处理请求出错: ${error.message}` });
    }
});

// 获取模板套用任务进度
app.get('/template-progress/:taskId', (req, res) => {
    const taskId = req.params.taskId;
    
    if (!templateTasks[taskId]) {
        return res.status(404).json({ error: '找不到指定的任务' });
    }
    
    const task = templateTasks[taskId];
    
    res.json({
        status: task.status,
        completed: task.completed,
        total: task.total,
        logs: task.logs,
        chapterStatus: task.chapterStatus,
        error: task.error,
        downloadPath: task.downloadPath,
        message: task.status === 'completed' ? '文档模板套用完成' : (task.error || '')
    });
});

// 模板套用任务处理函数
async function processTemplateTask(taskId) {
    const task = templateTasks[taskId];
    if (!task) return;
    
    try {
        task.status = 'processing';
        task.logs.push(`[INFO] 开始处理模板套用任务，使用模型: ${task.modelType}`);
        task.logs.push(`[INFO] 源文档: ${path.basename(task.uploadedFilePath)}`);
        task.logs.push(`[INFO] 模板类型: ${task.templateType}`);
        task.logs.push(`[INFO] 模板文件: ${path.basename(task.templateFilePath)}`);
        
        // 1. 从文档中提取内容
        task.logs.push(`[INFO] 开始从文档中提取章节内容...`);
        const extractedContent = await extractDocumentContent(task.uploadedFilePath, taskId);
        task.logs.push(`[SUCCESS] 文档内容提取完成`);
        
        // 2. 使用模板创建新的文档
        task.logs.push(`[INFO] 开始应用模板...`);
        const outputPath = await applyTemplate(task.templateFilePath, extractedContent, taskId);
        task.logs.push(`[SUCCESS] 模板应用完成`);
        
        // 3. 生成下载链接
        const timestamp = new Date().toISOString().replace(/[:.-]/g, '_');
        const fileName = `模板套用_${task.templateType}_${timestamp}.docx`;
        const downloadPath = path.join('/downloads', fileName);
        const outputDir = path.join(__dirname, 'public', 'downloads');
        
        if (!fs.existsSync(outputDir)) {
            fs.mkdirSync(outputDir, { recursive: true });
        }
        
        fs.copyFileSync(outputPath, path.join(__dirname, 'public', downloadPath));
        
        // 4. 更新任务状态
        task.status = 'completed';
        task.downloadPath = downloadPath;
        task.logs.push(`[SUCCESS] 文档模板套用完成，可下载生成的文档`);
        task.completed = task.total;
        
        // 清理临时文件
        try {
            fs.unlinkSync(outputPath);
        } catch (err) {
            console.error(`Failed to remove temp file ${outputPath}:`, err);
        }
    } catch (error) {
        console.error(`Template task ${taskId} processing error:`, error);
        task.status = 'failed';
        task.error = error.message;
        task.logs.push(`[ERROR] 模板套用失败: ${error.message}`);
        
        // 确保所有章节状态都已更新
        const allChapters = getAllChapterIds();
        allChapters.forEach(chapterId => {
            if (!task.chapterStatus[chapterId]) {
                task.chapterStatus[chapterId] = 'error';
            }
        });
    }
}

// 从文档中提取内容
async function extractDocumentContent(filePath, taskId) {
    const task = templateTasks[taskId];
    const extractedContent = {};
    
    // 获取所有章节ID
    const chapterIds = getAllChapterIds();
    
    // 并行处理多个章节，每次最多处理3个章节
    const batchSize = parseInt(process.env.MODEL_BATCH_SIZE) || 9; // 从环境变量获取批次大小，默认为3
    const batches = [];
    
    for (let i = 0; i < chapterIds.length; i += batchSize) {
        batches.push(chapterIds.slice(i, i + batchSize));
    }
    
    let completedCount = 0;
    
    for (const batch of batches) {
        // 并行处理当前批次的章节
        const batchResults = await Promise.all(
            batch.map(async chapterId => {
                // 更新章节状态
                task.chapterStatus[chapterId] = 'processing';
                task.logs.push(`[INFO] 开始处理章节: ${getChapterName(chapterId)}`);
                
                try {
                    const content = await extractChapterContent(filePath, chapterId, task.modelType);
                    
                    // 更新章节状态和计数
                    task.chapterStatus[chapterId] = 'completed';
                    completedCount++;
                    task.completed = completedCount;
                    task.logs.push(`[SUCCESS] 章节 ${getChapterName(chapterId)} 提取完成`);
                    
                    return { chapterId, content };
                } catch (error) {
                    task.chapterStatus[chapterId] = 'error';
                    task.logs.push(`[ERROR] 章节 ${getChapterName(chapterId)} 提取失败: ${error.message}`);
                    return { chapterId, error: error.message };
                }
            })
        );
        
        // 将批次结果添加到提取内容中
        for (const result of batchResults) {
            if (!result.error) {
                extractedContent[result.chapterId] = result.content;
            }
        }
    }
    
    return extractedContent;
}

// 应用模板
async function applyTemplate(templateFilePath, extractedContent, taskId) {
    const task = templateTasks[taskId];
    task.logs.push(`[INFO] 开始应用模板到文档...`);
    
    try {
        // 创建临时输出路径
        const timestamp = new Date().toISOString().replace(/[:.-]/g, '_');
        const outputPath = path.join(tempDir, `template_output_${timestamp}.docx`);
        
        // 读取模板文件
        const templateContent = fs.readFileSync(templateFilePath, 'binary');
        const zip = new PizZip(templateContent);
        
        // 设置图片处理器
        const imageModule = new ImageModule({
            centered: false,
            getImage: function(tagValue) {
                return fs.readFileSync(tagValue);
            },
            getSize: function() {
                return [300, 200]; // 默认图片尺寸
            }
        });
        
        // 创建docxtemplater实例
        const doc = new Docxtemplater(zip, {
            paragraphLoop: true,
            linebreaks: true,
            modules: [imageModule]
        });
        
        // 设置数据并渲染
        doc.render(formatContentForTemplate(extractedContent));
        
        // 生成输出文件
        const buffer = doc.getZip().generate({
            type: 'nodebuffer',
            compression: 'DEFLATE'
        });
        
        fs.writeFileSync(outputPath, buffer);
        
        task.logs.push(`[SUCCESS] 模板应用成功`);
        return outputPath;
    } catch (error) {
        task.logs.push(`[ERROR] 应用模板失败: ${error.message}`);
        throw error;
    }
}

// 为模板格式化内容
function formatContentForTemplate(extractedContent) {
    const templateData = {};
    
    // 处理每个章节的内容
    Object.keys(extractedContent).forEach(chapterId => {
        const content = extractedContent[chapterId];
        
        // 根据章节ID设置对应的模板变量
        if (content.status === 'found') {
            templateData[chapterId + '_content'] = content.content || '';
        } else {
            templateData[chapterId + '_content'] = ''; // 未找到章节内容
        }
    });
    
    return templateData;
}

// 调用CCB API
async function callCCBAPI(prompt) {
    // 实际实现需要调用CCB API
    // 这里返回模拟的响应
    return JSON.stringify({
        chapterId: "mock-chapter-id",
        chapterName: "模拟章节名称",
        content: "这是从CCB API返回的模拟内容。",
        status: "found"
    });
}

// 调用本地大模型
async function callLocalLLM(modelType, prompt) {
    // 实际实现需要调用本地大模型API
    // 这里返回模拟的响应
    return JSON.stringify({
        chapterId: "mock-chapter-id",
        chapterName: "模拟章节名称",
        content: `这是从本地模型 ${modelType} 返回的模拟内容。`,
        status: "found"
    });
}

// 提取特定章节的内容
async function extractChapterContent(filePath, chapterId, modelType) {
    // 构建提取章节的提示词
    const chapterName = getChapterName(chapterId);
    const prompt = buildChapterExtractionPrompt(chapterId, chapterName);
    
    // 从文档中提取文本内容
    const docText = await extractTextFromDoc(filePath);
    
    // 调用大模型进行内容提取
    const extractedContent = await callLargeModel(modelType, prompt, docText);
    
    return extractedContent;
}

// 构建章节提取的提示词
function buildChapterExtractionPrompt(chapterId, chapterName) {
    return `你是一位专业的文档分析专家，你需要从以下文档中精确提取"${chapterName}"章节的内容。

提取要求:
1. 仔细分析文档，找出标题为"${chapterName}"或与之近似含义的章节
2. 提取该章节的所有内容，包括文本、列表、表格描述
3. 保留原文的格式和结构，包括标题层级、列表格式等
4. 如果章节中包含图表描述，请标注"[图片:XXX]"占位符
5. 如果找不到相关章节，请返回"未找到章节内容"

返回格式:
{
  "chapterId": "${chapterId}",
  "chapterName": "${chapterName}",
  "content": "提取的章节内容...",
  "status": "found/not_found"
}

文档内容:
`;
}

// 从文档中提取文本内容
async function extractTextFromDoc(filePath) {
    try {
        const result = await mammoth.extractRawText({ path: filePath });
        return result.value;
    } catch (error) {
        throw new Error(`提取文档文本失败: ${error.message}`);
    }
}

// 调用大模型进行文本分析
async function callLargeModel(modelType, prompt, docText) {
    try {
        const fullPrompt = prompt + '\n\n文档内容:\n' + docText;
        
        let response;
        if (modelType.startsWith('deepseek') || modelType === 'DeepSeek-R1-QW32B_SFT_0228_V1') {
            response = await deepseekService.chatCompletion([
                { role: "user", content: fullPrompt }
            ]);
            
            try {
                // 尝试解析JSON响应
                const jsonResponse = JSON.parse(response.content);
                return jsonResponse;
            } catch (e) {
                // 如果不是JSON，尝试从文本中提取JSON
                const jsonMatch = response.content.match(/\{[\s\S]*\}/);
                if (jsonMatch) {
                    try {
                        return JSON.parse(jsonMatch[0]);
                    } catch (e2) {
                        throw new Error('无法从响应中提取有效的JSON数据');
                    }
                } else {
                    throw new Error('响应不包含有效的JSON数据');
                }
            }
        } else {
            // 调用CCBFZ模型
            response = await ccbfzService.reviewDocument(docText, prompt, modelType);
            try {
                return JSON.parse(response.result);
            } catch (e) {
                throw new Error('CCBFZ模型返回的数据格式无效');
            }
        }
    } catch (error) {
        throw new Error(`调用大模型失败: ${error.message}`);
    }
}

// 获取所有章节ID的辅助函数
function getAllChapterIds() {
    const chaptersWithSubsections = docSections.flatMap(section => {
        const subsectionIds = section.subsections ? section.subsections.map(sub => sub.id) : [];
        return [section.id, ...subsectionIds];
    });
    return chaptersWithSubsections;
}

// 根据ID获取章节名称的辅助函数
function getChapterName(chapterId) {
    // 先在顶级章节中查找
    for (const section of docSections) {
        if (section.id === chapterId) {
            return section.name;
        }
        
        // 再在子章节中查找
        if (section.subsections) {
            for (const subsection of section.subsections) {
                if (subsection.id === chapterId) {
                    return subsection.name;
                }
            }
        }
    }
    return '未知章节';
}

// 添加到server.js的结尾
app.get('/template-apply', (req, res) => {
    res.sendFile(path.join(__dirname, 'template_apply.html'));
});

module.exports = app;
